﻿#include "MaterialVectorOperationNodeModel.h"

#include "Object/NewObject.h"
#include "MaterialGraph/Pin/MaterialGraphPinModel.h"

void RMaterialVectorOperationNodeModel::FirstTimeInitializeObject()
{
    Super::FirstTimeInitializeObject();

    //添加输入输出
    for(int64_t i=0;i<3;++i)
    {
        auto VectorInput = NewObject<RMaterialGraphPinModel>(this);
        VectorInput->bIsOutput = false;
        VectorInput->ValueType = EMaterialGraphValueType::Vector3D;
        Inputs.Add(VectorInput);
    }
    
    auto ScalarInput = NewObject<RMaterialGraphPinModel>(this);
    ScalarInput->bIsOutput = false;
    ScalarInput->ValueType = EMaterialGraphValueType::Vector1D;
    Inputs.Add(ScalarInput);

    auto VectorOutput = NewObject<RMaterialGraphPinModel>(this);
    VectorOutput->bIsOutput = true;
    VectorOutput->ValueType = EMaterialGraphValueType::Vector3D;
    Outputs.Add(VectorOutput);

    auto ScalarOutput = NewObject<RMaterialGraphPinModel>(this);
    ScalarOutput->bIsOutput = true;
    ScalarOutput->ValueType = EMaterialGraphValueType::Vector1D;
    Outputs.Add(ScalarOutput);

    
    UpdatePinVisibility();
}

CString RMaterialVectorOperationNodeModel::GenerateExpression()
{
    switch (OperationType)
    {
        case EMaterialVectorOperationType::Add:
            return U"vec3 "+Outputs[0]->UniqueOutputPinName+U" = "
                +Inputs[0]->GetValueExpression()+U" + "
                +Inputs[1]->GetValueExpression()+U";\n";
        case EMaterialVectorOperationType::Subtract:
            return U"vec3 "+Outputs[0]->UniqueOutputPinName+U" = "
                +Inputs[0]->GetValueExpression()+U" - "
                +Inputs[1]->GetValueExpression()+U";\n";

        case EMaterialVectorOperationType::Multiply:
            return U"vec3 "+Outputs[0]->UniqueOutputPinName+U" = "
                +Inputs[0]->GetValueExpression()+U" * "
                +Inputs[1]->GetValueExpression()+U";\n";

        case EMaterialVectorOperationType::Divide:
            return U"vec3 "+Outputs[0]->UniqueOutputPinName+U" = "
                +Inputs[0]->GetValueExpression()+U" / "
                +Inputs[1]->GetValueExpression()+U";\n";

        case EMaterialVectorOperationType::AddAfterMultiply:
            return U"vec3 "+Outputs[0]->UniqueOutputPinName+U" = "
                +Inputs[2]->GetValueExpression()+U" + "
                +Inputs[1]->GetValueExpression()+U" * "
                +Inputs[0]->GetValueExpression()+U";\n";

        case EMaterialVectorOperationType::CrossProduct:
            return U"vec3 "+Outputs[0]->UniqueOutputPinName+U" = cross("
                +Inputs[0]->GetValueExpression()+U", "
                +Inputs[1]->GetValueExpression()+U");\n";

        case EMaterialVectorOperationType::DotProduct:
            return U"float "+Outputs[1]->UniqueOutputPinName+U" = dot("
                +Inputs[0]->GetValueExpression()+U", "
                +Inputs[1]->GetValueExpression()+U");\n";

        case EMaterialVectorOperationType::Project:
            return U"vec3 "+Outputs[0]->UniqueOutputPinName+U" = "
                +Inputs[0]->GetValueExpression()+U" * dot("
                +Inputs[0]->GetValueExpression()+U", "
                +Inputs[1]->GetValueExpression()+U") / dot("
                +Inputs[1]->GetValueExpression()+U", "
                +Inputs[1]->GetValueExpression()+U");\n";
        
        case EMaterialVectorOperationType::Reflect:
            return U"vec3 "+Outputs[0]->UniqueOutputPinName+U" = "
                +Inputs[0]->GetValueExpression()+U" - 2.0 * dot("
                +Inputs[0]->GetValueExpression()+U", "
                +Inputs[1]->GetValueExpression()+U") * "
                +Inputs[1]->GetValueExpression()+U";\n";
            
        case EMaterialVectorOperationType::Refract:
            return U"vec3 "+Outputs[0]->UniqueOutputPinName+U" = refract("
                +Inputs[0]->GetValueExpression()+U", "
                +Inputs[1]->GetValueExpression()+U", "
                +Inputs[2]->GetValueExpression()+U");\n";
        
        case EMaterialVectorOperationType::FaceForward:
            return U"vec3 "+Outputs[0]->UniqueOutputPinName+U" = "
                +U"dot("
                +Inputs[1]->GetValueExpression()+U", "
                +Inputs[2]->GetValueExpression()+U") < 0.0 ? "
                +Inputs[0]->GetValueExpression()+U" : -"
                +Inputs[0]->GetValueExpression()+U";\n";

        case EMaterialVectorOperationType::Distance:
            return U"float "+Outputs[1]->UniqueOutputPinName+U" = distance("
                +Inputs[0]->GetValueExpression()+U", "
                +Inputs[1]->GetValueExpression()+U");\n";
        
        case EMaterialVectorOperationType::Normalize:
            return U"vec3 "+Outputs[0]->UniqueOutputPinName+U" = normalize("
                +Inputs[0]->GetValueExpression()+U");\n";
        
        case EMaterialVectorOperationType::Length:
            return U"float "+Outputs[1]->UniqueOutputPinName+U" = length("
                +Inputs[0]->GetValueExpression()+U");\n";
            
        case EMaterialVectorOperationType::Scale:
            return U"vec3 "+Outputs[0]->UniqueOutputPinName+U" = "
                +Inputs[0]->GetValueExpression()+U" * "
                +Inputs[1]->GetValueExpression()+U";\n";
        
        case EMaterialVectorOperationType::Absolute:
            return U"vec3 "+Outputs[0]->UniqueOutputPinName+U" = abs("
                +Inputs[0]->GetValueExpression()+U");\n";

        case EMaterialVectorOperationType::Minimum:
            return U"vec3 "+Outputs[0]->UniqueOutputPinName+U" = min("
                +Inputs[0]->GetValueExpression()+U", "
                +Inputs[1]->GetValueExpression()+U");\n";

        case EMaterialVectorOperationType::Maximum:
            return U"vec3 "+Outputs[0]->UniqueOutputPinName+U" = max("
                +Inputs[0]->GetValueExpression()+U", "
                +Inputs[1]->GetValueExpression()+U");\n";

        case EMaterialVectorOperationType::Floor:
            return U"vec3 "+Outputs[0]->UniqueOutputPinName+U" = floor("
                +Inputs[0]->GetValueExpression()+U");\n";
        
        case EMaterialVectorOperationType::Ceil:
            return U"vec3 "+Outputs[0]->UniqueOutputPinName+U" = ceil("
                +Inputs[0]->GetValueExpression()+U");\n";
        
        case EMaterialVectorOperationType::Fraction:
            return U"vec3 "+Outputs[0]->UniqueOutputPinName+U" = fract("
                +Inputs[0]->GetValueExpression()+U");\n";

        case EMaterialVectorOperationType::Round:
            return U"vec3 "+Outputs[0]->UniqueOutputPinName+U" = round("
                +Inputs[0]->GetValueExpression()+U");\n";

        case EMaterialVectorOperationType::Mod:
            return U"vec3 "+Outputs[0]->UniqueOutputPinName+U" = mod("
                +Inputs[0]->GetValueExpression()+U", "
                +Inputs[1]->GetValueExpression()+U");\n";

        case EMaterialVectorOperationType::Loop:
            return U"vec3 "+Outputs[0]->UniqueOutputPinName+U" = "
                +U"mod("
                +Inputs[0]->GetValueExpression()+U" - "
                +Inputs[1]->GetValueExpression()+U", "
                +Inputs[2]->GetValueExpression()+U") + "
                +Inputs[1]->GetValueExpression()+U";\n";

        case EMaterialVectorOperationType::Snap:
            return U"vec3 "+Outputs[0]->UniqueOutputPinName+U" = "
                +U"round("
                +Inputs[0]->GetValueExpression()+U" / "
                +Inputs[1]->GetValueExpression()+U") * "
                +Inputs[1]->GetValueExpression()+U";\n";

        case EMaterialVectorOperationType::Sin:
            return U"vec3 "+Outputs[0]->UniqueOutputPinName+U" = sin("
                +Inputs[0]->GetValueExpression()+U");\n";
        
        case EMaterialVectorOperationType::Cos:
            return U"vec3 "+Outputs[0]->UniqueOutputPinName+U" = cos("
                +Inputs[0]->GetValueExpression()+U");\n";

        case EMaterialVectorOperationType::Tan:
            return U"vec3 "+Outputs[0]->UniqueOutputPinName+U" = tan("
                +Inputs[0]->GetValueExpression()+U");\n";
                
        default:
            assert(false);
    };
}

void RMaterialVectorOperationNodeModel::UpdatePinVisibility()
{
    switch(OperationType)
    {
        case EMaterialVectorOperationType::Absolute:
        case EMaterialVectorOperationType::Floor:
        case EMaterialVectorOperationType::Ceil:
        case EMaterialVectorOperationType::Fraction:
        case EMaterialVectorOperationType::Round:
        case EMaterialVectorOperationType::Normalize:
        case EMaterialVectorOperationType::Sin:
        case EMaterialVectorOperationType::Cos:
        case EMaterialVectorOperationType::Tan:
        {
            //需要一个向量输入和一个向量输出
            Inputs[0]->bIsHidden = false;
            Inputs[1]->bIsHidden = true;
            Inputs[2]->bIsHidden = true;
            Inputs[3]->bIsHidden = true;
            Outputs[0]->bIsHidden = false;
            Outputs[1]->bIsHidden = true;
            break;
        }

        case EMaterialVectorOperationType::Add:
        case EMaterialVectorOperationType::Subtract:
        case EMaterialVectorOperationType::Multiply:
        case EMaterialVectorOperationType::Divide:
        case EMaterialVectorOperationType::CrossProduct:
        case EMaterialVectorOperationType::Project:
        case EMaterialVectorOperationType::Reflect:
        case EMaterialVectorOperationType::Minimum:
        case EMaterialVectorOperationType::Maximum:
        case EMaterialVectorOperationType::Mod:
        case EMaterialVectorOperationType::Snap:
        {
            //需要两个向量输入和一个向量输出
            Inputs[0]->bIsHidden = false;
            Inputs[1]->bIsHidden = false;
            Inputs[2]->bIsHidden = true;
            Inputs[3]->bIsHidden = true;
            Outputs[0]->bIsHidden = false;
            Outputs[1]->bIsHidden = true;
            break;
        }

        case EMaterialVectorOperationType::AddAfterMultiply:
        case EMaterialVectorOperationType::FaceForward:
        case EMaterialVectorOperationType::Loop:
        {
            //需要三个向量输入和一个向量输出
            Inputs[0]->bIsHidden = false;
            Inputs[1]->bIsHidden = false;
            Inputs[2]->bIsHidden = false;
            Inputs[3]->bIsHidden = true;
            Outputs[0]->bIsHidden = false;
            Outputs[1]->bIsHidden = true;
            break;
        }

        case EMaterialVectorOperationType::Length:
        {
            //需要一个向量输入和一个浮点输出
            Inputs[0]->bIsHidden = false;
            Inputs[1]->bIsHidden = true;
            Inputs[2]->bIsHidden = true;
            Inputs[3]->bIsHidden = true;
            Outputs[0]->bIsHidden = true;
            Outputs[1]->bIsHidden = false;
            break;
        }

        case EMaterialVectorOperationType::DotProduct:
        case EMaterialVectorOperationType::Distance:
        {
            //需要两个向量输入和一个浮点输出
            Inputs[0]->bIsHidden = false;
            Inputs[1]->bIsHidden = false;
            Inputs[2]->bIsHidden = true;
            Inputs[3]->bIsHidden = true;
            Outputs[0]->bIsHidden = true;
            Outputs[1]->bIsHidden = false;
            break;
        }

        case EMaterialVectorOperationType::Scale:
        {
            //需要一个向量输入和一个浮点输入和一个向量输出
            Inputs[0]->bIsHidden = false;
            Inputs[1]->bIsHidden = true;
            Inputs[2]->bIsHidden = true;
            Inputs[3]->bIsHidden = false;
            Outputs[0]->bIsHidden = false;
            Outputs[1]->bIsHidden = true;
            break;
        }

        case EMaterialVectorOperationType::Refract:
        {
            //需要两个向量输入和一个浮点输入和一个向量输出
            Inputs[0]->bIsHidden = false;
            Inputs[1]->bIsHidden = false;
            Inputs[2]->bIsHidden = true;
            Inputs[3]->bIsHidden = false;
            Outputs[0]->bIsHidden = false;
            Outputs[1]->bIsHidden = true;

            break;
        }

        default:
        {
            assert(false);
        }
    }

}
